/**
 * class FileHandler
 * 
 * FileHandler is the class that actually stores the data in permanent storage
 * Data  is stored in files that are XML coded
 * Filehandler is used primarily for simple reading and writing of files to XML or txt
 * 
 * @author Ankit Ganla
 * @author Vaarnan Drolia
 */

package storage;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import core.Log;




public class FileHandler
{
	private static final String CURRENT_DIRECTORY = ".";
	private static final String textFile = "EventsInDatabase.txt";

	/**
	 * 
	 * @param o
	 * @param filename
	 * @return boolean- true if object is written to XML, false if otherwise
	 */
	public static boolean writeObject(Object o, String filename) {
	Log.debug("FH: Entering the writeObject function");

		try {
			BufferedOutputStream xmlOut = new BufferedOutputStream(
					new FileOutputStream(filename));
			XMLEncoder writeToXml = new XMLEncoder(xmlOut);
			writeToXml.writeObject(o);
			writeToXml.close();
			Log.debug("FH: writeObject wrote the eventlist to xml file");
			return true;
		} catch (FileNotFoundException e) {
			Log.error("FileHandler: File not found exception in writeobject function");
			return false;
		}
	}

	/**
	 * 
	 * @param filename
	 * @return Returns an EventList object
	 */
	public static Object readObject(String filename) {
		Log.debug("FH: Entering the readObject function");
		try {
			BufferedInputStream xmlIn = new BufferedInputStream(
					new FileInputStream(filename));
			XMLDecoder readFromXml = new XMLDecoder(xmlIn);
			Object o = readFromXml.readObject();
			readFromXml.close();
			Log.debug("FH: readObject read the list from the xml file");
			return o;
		} catch (FileNotFoundException e) {
			Log.error("FileHandler: file not found in readobject function");
			return null;
		}
	}

	
	/**
	 * 
	 * @return boolean- true if old file is deleted and new empty file is created. false if otherwise
	 */
	public static boolean removeOldText() {
		Log.debug("FH: Entering the removeOldText function");
		try {
			File newFile = new File(textFile);
			if(!newFile.createNewFile()) {
				if(!newFile.delete()) {
					Log.warn("FH: in removeOldText. Delete of old file was unsuccessful");
					return false;
				}
			} 
		} catch (IOException ignore) {}
		return true;
	}

	
	/**
	 * 
	 * @param stringToBeWritten
	 */
	public static void writeToText(String stringToBeWritten) {
		Log.debug("FH: Entering the writeToText function with only string param");
		writeToText(stringToBeWritten, textFile);
	}

	/**
	 * 
	 * @param stringToWrite
	 * @param textFile
	 */
	public static void writeToText(String stringToWrite, String textFile) {
		Log.debug("FH: Entering the writeToText function with " + stringToWrite + "," + textFile);
		try {
			BufferedWriter textWriter = new BufferedWriter(new FileWriter(textFile, false));
			textWriter.write(stringToWrite);
			textWriter.newLine();
			textWriter.close();
			Log.debug("FH: In writeToText. Line was successfully written to the file");
		} catch (IOException e) {
			Log.error("FileHandler: Error when writing to file in writeToText");
		}
	}

	/**
	 * 
	 * @param dataFile
	 * @return String- sha1 Checksum 
	 */
	public static String getSha1Checksum(String dataFile) {
		Log.debug("FH: Entering the createSha1Checksum function");

		MessageDigest md;
		try {
			md = MessageDigest.getInstance("SHA1");
		} catch (NoSuchAlgorithmException e) {
			Log.error("FileHandler: No such algorithm in createSha1CheckSum. ");
			return null;
		}
		FileInputStream fis;
		try {
			fis = new FileInputStream(dataFile);
		} catch (FileNotFoundException e) {
			Log.error("FileHandler: File not found in check1CheckSum");
			return null;
		}
		byte[] dataBytes = new byte[1024];

		int nread = 0; 

		try {
			while ((nread = fis.read(dataBytes)) != -1) {
				md.update(dataBytes, 0, nread);
			}
		} catch (IOException e) {
			Log.error("FileHandler: IOException in reading file in check1CheckSum");
			return null;
		}

		byte[] mdbytes = md.digest();

		StringBuffer sb = new StringBuffer("");
		for (int i = 0; i < mdbytes.length; i++) {
			sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
		}

		return sb.toString();
	}

	/**
	 * 
	 * @param zipFile
	 * @return boolean- true if file is zipped, false otherwise
	 */
	public static boolean unzipFile(String zipFile) {
		Log.debug("FH: Entering the unzipFile function with one param");
		return unzipFile(zipFile, CURRENT_DIRECTORY);
	}
	
	/**
	 * 
	 * @param zipFile
	 * @param path
	 * @return boolean- true is file is unzipped, false otherwise
	 */
	public static boolean unzipFile(String zipFile, String path) {
		Log.debug("FH: Entering the unzipFile function with 2 params");
		int BUFFER = 2048;
		try {
			File file = new File(zipFile);
			ZipFile zip = new ZipFile(file);
			String newPath = path;
			
			Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();

			Log.debug("FH: Unzip function will process each entry now");
			while (zipFileEntries.hasMoreElements())
			{
				ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
				String currentEntry = entry.getName();
				File destFile = new File(newPath, currentEntry);
				
				File destinationParent = destFile.getParentFile();
				
				
				if (!destinationParent.getName().equals(newPath)) {
					destinationParent.mkdirs();
					Log.warn("FH: parent directory structure needed to be created");
				}

				if (!entry.isDirectory())
				{
					BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
					int currentByte;
					byte data[] = new byte[BUFFER];

					Log.debug("FH: in unzip. writing the current file to disk");
					FileOutputStream fos = new FileOutputStream(destFile);
					BufferedOutputStream dest = new BufferedOutputStream(fos,
							BUFFER);

					while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
						dest.write(data, 0, currentByte);
					}
					dest.flush();
					dest.close();
					is.close();
				}
			}
		} catch (FileNotFoundException e) {
			Log.error("FileHandler: File not found in unzip function");
			return false;
		} catch (IOException e) {
			Log.error("Filehandler: IOException in unzip function");
			return false;
		}
		return true;
	}
}
